/**
 * 用户登录及登录日志管理
 * @author lms 2022.07.22
 */
package ms.core.gateway.service;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import ms.core.common.ApiResult;
import ms.core.common.Page;
import ms.core.common.RequestBody;
import ms.core.common.consts.SysErr;
import ms.core.common.utils.RequestUtils;
import ms.core.gateway.bean.SysLoginBean;
import ms.core.gateway.request.LoginRequest;
import ms.core.gateway.response.SysUserVO;
import ms.core.gateway.system.Application;
import ms.core.gateway.tool.CookieTool;
import ms.core.gateway.tool.CookieTool.Cookies;
import ms.core.gateway.tool.SessionTool;
import ms.core.gateway.tool.SessionTool.Session;
import ms.core.gateway.utils.SysLoginUtils;
import ms.core.tool.*;

public class LoginService {
	
	/**
	 * 接口：<div style="color:red"><b> needCode </b></div><p>
	 * 登录是否需要验证码<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body
	 * @param sid
	 * @return
	 */
	public ApiResult needCode(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		Map<String, Object> data = new HashMap<>();
		data.put("captcha", Application.CODE_LOGIN);
		return new ApiResult(true).setData(data);
	}

	/**
	 * 接口：<div style="color:red"><b> needCode </b></div><p>
	 * 功能：生成验证码图片,并建立会话<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body
	 * @param sid
	 * @return
	 */
	public ApiResult getCode(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		try {
			resp.setContentType("image/jpeg");
			String code = CheckCodeTool.generateCheckCode(Application.CODE_LENGTH);
			if (StrTool.isBlank(sid)) sid = CookieTool.getSid(req, resp);
			// 设置验证码到用户会话
			SessionTool.setCheckCode(sid, RequestUtils.getRequestRemoteIp(req), code);
			ServletOutputStream ros = resp.getOutputStream();
			CheckCodeTool.outputImage(200, 80, ros, code);
			ros.flush();
			ros.close();
		} catch(Exception e) {			
			Log4j2Tool.error(LoginService.class, e);
		}
		return null;
	}

	/**
	 * 接口：<div style="color:red"><b> getCodeEx </b></div><p>
	 * 功能：获取是否需要验证码，如需验证码，返回验证码图(图片经过base64加密)<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body
	 * @param sid
	 * @return
	 */
	public ApiResult getCodeEx(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		try {
			ApiResult ret = new ApiResult(true);
			ret.setCode(SysErr.CODE_SUCCESS);
			
			Map<String, Object> data = new HashMap<>();
			data.put("captcha", Application.CODE_LOGIN);
			if (Application.CODE_LOGIN) {
				String code = CheckCodeTool.generateCheckCode(Application.CODE_LENGTH);
				if (StrTool.isBlank(sid)) sid = CookieTool.getSid(req, resp);
				//设置验证码到用户会话
				SessionTool.setCheckCode(sid, RequestUtils.getRequestRemoteIp(req), code);
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				CheckCodeTool.outputImage(200, 80, baos, code);
				baos.flush();
				data.put("img", EncryptTool.base64Encode(baos.toByteArray()));
				baos.close();
			}
			ret.setData(data);

			return ret;
		} catch(Exception e) {			
			Log4j2Tool.error(LoginService.class, e);
			return new ApiResult(false, SysErr.SYS_UNKNOWN_ERR, "获取验证码失败.");
		}
	}
	
	/**
	 * 接口：<div style="color:red"><b> login </b></div><p>
	 * 功能: 用户登录<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body={"account":"用户帐号","password":"密码","code":"验证码"}
	 * @param sid
	 * @return
	 */
	public ApiResult login(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		// 不需要验证码登录,sid在登录时是空的
		if (StrTool.isBlank(sid)) sid = CookieTool.getSid(req, resp);

		// 判断是否是已登录用户(session和cookie双重验证)
		if (SessionTool.logined(sid) && CookieTool.logined(req))
			return new ApiResult(true, SysErr.USER_RELOGIN);

		LoginRequest loginReq = JsonTool.objectToBean(body, LoginRequest.class);
		// 参数校验
		if (loginReq==null || !loginReq.verify())
			return new ApiResult(false, SysErr.SYS_PARAM_ERR);

		// 获取缓存的验证码，取走后需让原验证码失效
		String cacheCode = SessionTool.getCheckCode(sid);
		SessionTool.setCheckCode(sid, RequestUtils.getRequestRemoteIp(req), SysTool.makeUUID(""));

		// 判断登录是否需要验证码以及验证码是否正确
		if (Application.CODE_LOGIN && (StrTool.isBlank(loginReq.getCode()) || !loginReq.getCode().equalsIgnoreCase(cacheCode)))
			return new ApiResult(false, SysErr.SYS_CHECKCODE_ERR);

		String visitIp = RequestUtils.getRequestRemoteIp(req);
		SysLoginUtils utils = new SysLoginUtils();
		SysUserVO usr = utils.login(loginReq.getAccount(), loginReq.getPassword(), visitIp);

		// 保存会话
		usr.setVisitIp(visitIp);
		SessionTool.setLoginSession(sid, usr);
		// 设置cookie
		Cookies cks = CookieTool.setLoginCookies(resp, sid, usr.getCorpUid(), usr.getUid(), usr.getAccount(), usr.getNick());
		if (Application.userDelegate!=null) {
			Application.userDelegate.formatUser(usr);
			Session ssn = SessionTool.getSession(sid);
			Application.userDelegate.afterLogin(ssn, cks);
		}

		return new ApiResult(true).setData(usr);
	}

	/**
	 * 接口：<div style="color:red"><b> status </b></div><p>
	 * 功能: 获取用户登录状态，并实现cookie和session续期<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body
	 * @param sid
	 * @return
	 */
	public ApiResult status(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		// 判断是否是已登录用户(session和cookie双重验证)
		if (StrTool.isBlank(sid) || !SessionTool.logined(sid) || !CookieTool.logined(req))
			return new ApiResult(false, SysErr.USER_NOT_LOGIN);

		Session ssn = SessionTool.getSession(sid);
		Map<String, Object> data = JsonTool.objectToMap(ssn, false);		
		data.remove("ext");// ext对象保存会话验证码等信息，需移除
		// cookie续期
		CookieTool.setLoginCookies(resp, sid, ssn.getCorpUid(), ssn.getUserUid(), ssn.getAccount(), ssn.getNick());
		// session续期
		SessionTool.updateLastVisit(sid, RequestUtils.getRequestRemoteIp(req));

		return new ApiResult(true).setData(data);
	}

	/**
	 * 接口：<div style="color:red"><b> logout </b></div><p>
	 * 功能: 退出登录<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body
	 * @param sid
	 * @return
	 */
	public ApiResult logout(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		SessionTool.invalidSession(sid);
		CookieTool.invalidCookie(resp);
		return new ApiResult(true);
	}

	/**
	 * 接口：<div style="color:red"><b> delLog </b></div><p>
	 * 功能：删除登录日志<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body={"ids":"逗号分隔的日志id"}<p>
	 * @param sid
	 * @return
	 */
	public ApiResult delLogin(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		RequestBody rb = RequestBody.getInstance(body);
		String ids = rb.getStrValue("ids", null);
		if (StrTool.isBlank(ids)) 
			return new ApiResult(false, SysErr.SYS_PARAM_ERR);
	
		String uid = SessionTool.getUserUid(sid);
		SysLoginUtils utils = new SysLoginUtils();
		int ret = utils.deleteLoginLog(uid, ids);
		return new ApiResult(true, SysErr.CODE_SUCCESS, "删除日志:"+ret);
	}

	/**
	 * 接口：<div style="color:red"><b> qryLogin </b></div><p>
	 * 功能: 登录日志查询<p>
	 * 
	 * @param req
	 * @param resp
	 * @param body={"corpUid":"企业Id","account":"用户帐号","page":页码,"pagesize":页大小}
	 * @param sid
	 * @return
	 */
	public ApiResult qryLogin(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> body, String sid) {
		RequestBody rb = RequestBody.getInstance(body);
		String account   = rb.getStrValue("account", null);
		String corpUid  = SessionTool.getCorpUid(sid);
		int page = rb.getIntValue("page", 1);
		if (page<1) page = 1;
		int pagesize = rb.getIntValue("pagesize", Application.PAGE_SIZE);
		if (pagesize<1 || pagesize>100) pagesize = Application.PAGE_SIZE;

		if (StrTool.isBlank(corpUid) || !SessionTool.isSuperCorp(sid)) 
			corpUid = SessionTool.getCorpUid(sid);

		SysLoginUtils utils = new SysLoginUtils();
		Page<SysLoginBean> pg = utils.queryLoginLog(corpUid, account, page, pagesize);
		return new ApiResult(true).setPage(pg);
	}
}
